package com.newy.service.sys;

import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.newy.core.AppException;
import com.newy.core.BaseService;
import com.newy.core.util.LangUtils;
import com.newy.core.web.AppContext;
import com.newy.dao.sys.MenuDao;
import com.newy.dao.sys.RoleMenuDao;
import com.newy.domain.sys.Menu;
import com.newy.domain.sys.Menu.MenuType;
import com.newy.domain.sys.User;
import com.newy.domain.sys.vo.MenuItem;


@Service
@Transactional
public class MenuService  extends BaseService{
	//存放js模块文件的根路径
	private static final String JS_MODULE_ROOT = "/js-modules";
	
	@Resource private MenuDao menuDao;
	@Resource private RoleMenuDao roleMenuDao;
	
	@Transactional(readOnly=true)
	public Menu getMenu(Long id){
		return menuDao.get(id);
	}
	
	@Transactional(readOnly=true)
	public String getIdPath(Long id){
		return menuDao.getIdPath(id);
	}
	
	public void saveMenuFolder(Menu menu){
		menu.setType(MenuType.FOLDER);
		this.save(menu);
	}
	
	/**
	 * 保存菜单项，包含了菜单项的按钮。
	 * 将此方法设为同步，避免多人同时修改时出错。
	 */
	public synchronized void saveMenuItem(MenuItem menuItem){
		//保存菜单项
		Menu item = menuItem.getMenuItem();
		item.setType(MenuType.ITEM);
		this.save(item);
		//处理菜单项的按钮
		List<Menu> oldButtons = menuDao.getChildren(item.getId(), MenuType.BUTTON);
		//逐个检查新按钮并保存
		Double btnSortNum = 1.0;
		for (Menu newBtn : menuItem.getButtons()){
			newBtn.setParentId(item.getId());
			newBtn.setType(MenuType.BUTTON);
			newBtn.setSortNum(btnSortNum ++); //重新设置排序号
			//在旧按钮中查找，找到就是修改，找不到就是添加（不要用判断id是否要null的方法，那样多人修改时不准确）
			int i = oldButtons.indexOf(newBtn);
			if (i > -1){
				//BeanUtils.copyProperties(newBtn, oldButtons.get(i)); //将新按钮的数据复制到旧按钮中，让hibernate自动保存
				menuDao.evict(oldButtons.get(i)); //从当前session中移除实体，才不影响下面的保存
				oldButtons.remove(i); //从旧按钮中删除，剩余的按钮要从数据库中删除
			}
			else{
				newBtn.setId(null); //避免id已存在的情况
			}
			this.save(newBtn);
		}
		//删除不需要的按钮
		for (Menu button: oldButtons){
			this.delete(button.getId());
		}
	}
	
	/**
	 * 保存实体，在保存之前做检验
	 */
	private void save(Menu menu){
		if (menu.isItem()){ //菜单项的name必须唯一
			if (!menuDao.isPropsUnique(menu, "name"))
				throw new AppException("保存失败：菜单项名称“%s”已存在", menu.getName());
		}
		else if (menu.isButton()){ //按钮的name在同一个菜单下必须唯一
			if (!menuDao.isPropsUnique(menu, "name", "parentId"))
				throw new AppException("保存失败：按钮名称“%s”有重复", menu.getName());
		}
		else{
			if (menu.getId() != null && menu.getId().equals(menu.getParentId()))
				throw new AppException("保存失败：父目录不能是自身");
		}
		menuDao.save(menu);
	}
	
	/**
	 * 删除菜单，注意是级联删除，同时删除所有与菜单关联的角色关系
	 */
	public void delete(Long[] ids){
		for (Long id : ids){
			this.delete(id);
		}
	}
	
	/**
	 * 级联删除菜单，同时删除与角色的关联关系
	 */
	public void delete(Long id){
		Set<Long> ids = menuDao.deleteCascade(id);
		for (Long subId : ids){
			roleMenuDao.clearRolesFromMenu(subId);
		}
	}

	@Transactional(readOnly=true)
	public List<Menu> getAllMenus(){
		return menuDao.getAll();
	}
	
	@Transactional(readOnly=true)
	public List<Menu> getAllChildren(Long parentId){
		return menuDao.getChildren(parentId);
	}
	
	/**
	 * 获取指定目录下面的所有目录和菜单项
	 */
	@Transactional(readOnly=true)
	public List<Menu> getMenuChildren(Long parentId){
		return menuDao.getChildren(parentId, MenuType.FOLDER, MenuType.ITEM);
	}

	/**
	 * 获取指定目录下面的所有菜单目录
	 */
	@Transactional(readOnly=true)
	public List<Menu> getMenuFolderChildren(Long parentId){
		return menuDao.getChildren(parentId, MenuType.FOLDER);
	}
	
	/**
	 * 获取指定菜单项的所有按钮
	 */
	@Transactional(readOnly=true)
	public List<Menu> getMenuButtons(Long menuId){
		return menuDao.getChildren(menuId, MenuType.BUTTON);
	}

	/**
	 * 获取用户权限内的所有菜单，菜单按父子顺序排好
	 */
	@Transactional(readOnly=true)
	public List<Menu> getMenusOfUser(User user){
		if (user.getIsAdmin()){ //超级管理员没有限制
			return menuDao.getAll();
		}
		else{
			return menuDao.getMenusOfUser(user.getId());
		}
	}
	
	@Transactional(readOnly=true)
	public boolean hasChildren(Long parentId){
		return menuDao.hasChildren(parentId);
	}

	/**
	 * 根据js模块的父路径，获取下一层的子目录和子文件
	 */
	@Transactional(readOnly=true)
	public File[] getJsModules(final String parentPath){
		//js模块的存放路径
		File parent = new File(AppContext.getWebRealPath(JS_MODULE_ROOT + "/" + parentPath));
		//获取子目录和文件
		File[] files = parent.listFiles(new FileFilter(){
			@Override
			public boolean accept(File file) {
				//去掉第一层app目录
				if (LangUtils.isEmpty(parentPath) && file.getName().equals("app")) return false;
				//去掉不以Panel.js结束的文件
				return file.isDirectory() || file.getName().endsWith("Panel.js");
			}
		});
		//排序，目录在前，文件在后
		Arrays.sort(files, new Comparator<File>(){
			@Override
			public int compare(File o1, File o2) {
				if (o1.isFile() && o2.isDirectory())
					return 1;
				return 0;
			}
		});
		return files;
	}
}
